#!/usr/bin/env python
#

# aiv api
#
#from xbmcswift2 import Plugin
import bottlenose
import time
import os
from xml.dom import minidom
from xml.dom import pulldom

# auto play URL
# http://www.amazon.co.uk/gp/product/B00J9G6320/ref=dv_web_wtls_list_ovl_rs__wnzw

# globals
#
# log handle should really be passed in by calling
#plugin = Plugin()
#plugin.log.setLevel('DEBUG')
#
BNODE_ROOT   = { 'id': '3010085031', 'name': 'Amazon Instant Video', 'children': [] }
#BNODE_ROOT   = { 'id': '3280626031', 'name': 'Prime Instant Video', 'children': [] }

AIV_GENRES     = []
AIV_CATEGORIES = []
AIV_TXT    = { 'genres':       'Featured Categories',
               'prime_cat':    'Prime Instant Video',
               'prime_tv':     'TV',
               'prime_movies': 'Movies'}


class AIVApi():

    def __init__(self,plugin,force=False):
        self.plugin = plugin
        self._init_amazon()
        self._init_hierarchy(force)

    def _init_amazon(self):

        # For Debug:
        # Will read AWS details from env vars
        #   AWS_ACCESS_KEY_ID
        #   AWS_SECRET_ACCESS_KEY
        #   AWS_ASSOCIATE_TAG
        # xbmcswift2 will not prompt for values if
        #   AWS_DETAILS=1
        if os.environ.get('AWS_DETAILS') == "1":
            self.plugin.log.debug("Reading AWS detail from env")
            self.amazon = bottlenose.Amazon(Region = 'UK')
            return

        setting = {}
        for setting_name in ('aws_key_id','aws_key','aws_tag'):
            setting[setting_name] = self.plugin.get_setting(setting_name, str)
            if not setting[setting_name]:
                self.plugin.log.warning("unset aws details")
                # TODO: show error message and settings screen
                # move section out of API

        self.plugin.log.debug(setting)
        self.amazon = bottlenose.Amazon(AWSAccessKeyId     = setting['aws_key_id'],
                                        AWSSecretAccessKey = setting['aws_key'],
                                        AssociateTag       = setting['aws_tag'],
                                        Region             = 'UK')


    def _init_hierarchy(self,force=False):
        # try getting hierarchy from cache
        self.hierarchy = self.plugin.get_storage('hierarchy')

        if self.hierarchy.keys() and force is False:
            self.plugin.log.info("hierarchy loaded from cache")
            return

        self.plugin.log.info("refreshing hierarchy")
        hierarchy = BNODE_ROOT.copy()
        self._traverse_hierarchy(hierarchy)

        # cache result - so we don't have to do this all the time
        self.plugin.log.info("caching hierarchy")
        self.hierarchy.clear()
        self.hierarchy.update(hierarchy)
        self.hierarchy.sync()

    #
    # get browsenodes for Movie/TV searches
    # to allow for better results we need two:
    # one to restrict to prime tv or movies
    # one to restrict to genres (called featured categories)
    #
    # browse node hierarchy on amazon uk under the amazon instant vid
    # root node currentlylooks something similar to this
    #
    # 3010085031: Amazon Instant Video
    # ...
    #         3280626031: Prime Instant Video
    #           3356010031: Movies
    #           3356011031: TV
    #   3010087031: Featured Categories
    #     3064071031: Anime
    #     3064074031: Comedy
    # ...
    #   3010086031: Categories
    #     3046738031: TV
    #     3046737031: Movies
    #
    # use traverse(root node) to print hierarchy if this breaks.
    # we'll discover the nodes for anything under AIV dynamically,
    # as they may change
    #
    # storage obj for browsenode hierarchy will be
    # bnode: {id: <id>, name: <name>, children: <children> }
    # children:   [ <bnode>,<bnode>,... ]
    #
    def _search_hierarchy(self,bnode,searchterms):
        results = []
        if bnode['name'] in searchterms:
            results.append(bnode)
        for child in bnode['children']:
            found = self._search_hierarchy(child,searchterms)
            if len(found):
                results.extend(found)
        return results


    def _init_bnodes(self):

        # dont initialise AIV_BNODES more than once
        if len(AIV_GENRES):
            self.plugin.log.debug('aiv bnodes already initialised')
            return

        # get tv/movie browsenodes
        categories = []
        prime_bnodes = self._search_hierarchy(self.hierarchy,[AIV_TXT['prime_cat']])
        for bnode in prime_bnodes:
            categories.extend(self._search_hierarchy(bnode,[AIV_TXT['prime_tv'],AIV_TXT['prime_movies']]))

        # get genre browsenodes
        genres = []
        genre_bnodes = self._search_hierarchy(self.hierarchy,[AIV_TXT['genres']])
        for bnode in genre_bnodes:
            genres.extend(bnode['children'])

        AIV_CATEGORIES.extend(categories)
        AIV_GENRES.extend(genres)
        self.plugin.log.debug(AIV_CATEGORIES)
        self.plugin.log.debug(AIV_GENRES)
        return


    def _get_bnode(self,category):

        # ensure nodes are loaded
        self._init_bnodes()

        for bnode in AIV_CATEGORIES:
            if bnode['name'].lower() == category.lower():
                return bnode['id']

        return ''


    def _get_genres(self):

        # ensure nodes are loaded
        self._init_bnodes()

        genres = {}
        for bnode in AIV_GENRES:
            genres[bnode['name']] = bnode['id']

        return genres


    # bnode: { id: <id>, name: <name>, children: <children> }
    # children:   [ <bnode>,<bnode>,... ]
    def _traverse_hierarchy(self,bnode):

        self.plugin.log.debug('traversing: '+bnode['name'])

        # get children
        response = self.amazon.BrowseNodeLookup(BrowseNodeId=bnode['id'], ResponseGroup='BrowseNodeInfo')
        children = _xml_parse_browsenodelookup(response)
        for child in children:
            # traverse children
            time.sleep(1)
            self._traverse_hierarchy(child)
        bnode['children'] = children


    def _print_hierarchy_pretty(self,bnode,prefix=''):
        print(prefix+bnode['id']+': '+bnode['name'])
        for child in bnode['children']:
            self._print_hierarchy_pretty(child,prefix+'  ')
            

    def do_search(self,category,search_term,page=1):
        return self._item_search(category,sort_mode='relevance',title=search_term,page=page)


    def get_new(self,category,page="1"):
        return self._item_search(category,sort_mode='new',page=page)


    def get_popular(self,category,page="1"):
        return self._item_search(category,sort_mode='popular',page=page)


    def get_genre(self,category,genre,page=1):
        return self._item_search(category,sort_mode='popular',keywords=genre,page=page)


    def _item_search(self,category,sort_mode="",keywords="",title="",page="1"):

        bnode = self._get_bnode(category)

        sort_options = {'popular':   'popularity-rank',
                        'new':       'date-desc-rank',
                        'relevance': 'relevancerank'}

        if sort_mode not in sort_options.keys():
            self.plugin.log.debug("Invalid sort method, using default")
            sort_mode = ""
        else:
            sort_mode = sort_options[sort_mode]

        response = self.amazon.ItemSearch(BrowseNode=bnode,
                                          ItemPage=page,
                                          SearchIndex='UnboxVideo',
                                          ResponseGroup='Small',
                                          Sort=sort_mode,
                                          Keywords=keywords,
                                          Title=title)

        self.plugin.log.debug(_xml_print(response))
        num_pages,items = _xml_parse_itemsearch(response)

        self.plugin.log.debug("num_pages: "+num_pages)
        self.plugin.log.debug("items: "+str(items))

        num_pages = int(num_pages)
        page      = int(page)
        
        if page < 10 and page < num_pages:
            next_page=True
        else:
            next_page=False

        return items,next_page



# xml utilities
#
def _xml_get_text(nodelist):
    rc = []
    for node in nodelist:
        if node.nodeType == node.TEXT_NODE:
            rc.append(node.data)
    return ''.join(rc)

def _xml_print(string):
    node = minidom.parseString(string)
    return node.toprettyxml().encode('ascii','ignore')


def _xml_parse_itemsearch(string):
    items = []
    stream = pulldom.parseString(string)
    for event, node in stream:
        if event == pulldom.START_ELEMENT and node.tagName == 'TotalPages':
            stream.expandNode(node)
            num_pages = _xml_get_text(node.childNodes)
        if event == pulldom.START_ELEMENT and node.tagName == 'Item':
            stream.expandNode(node)
            asin  = _xml_get_text(node.getElementsByTagName('ASIN')[0].childNodes)
            title = _xml_get_text(node.getElementsByTagName('Title')[0].childNodes)
            items.append({'title': title,'asin': asin})
    return (num_pages,items)


def _xml_parse_browsenodelookup(string):
    bnodes = []
    stream = pulldom.parseString(string)
    for event, node in stream:
        if event == pulldom.START_ELEMENT and node.tagName == 'Children':
            stream.expandNode(node)
            for bnode in node.getElementsByTagName('BrowseNode'):
                bid  = _xml_get_text(bnode.getElementsByTagName('BrowseNodeId')[0].childNodes)
                name = _xml_get_text(bnode.getElementsByTagName('Name')[0].childNodes)
                bnodes.append({'id': bid, 'name': name, 'children': []})
    return bnodes


def _test():
#    amazon = bottlenose.Amazon(Region = 'UK')
#    response = amazon.ItemSearch(SearchIndex='UnboxVideo', ResponseGroup='Small', Title='vampire+slayer')
    #response = amazon.ItemLookup(ItemId="B00QU0YREE",ResponseGroup="Large");
    #response = amazon.BrowseNodeLookup(BrowseNodeId='3010086031,3010088031', ResponseGroup='BrowseNodeInfo')
    api._item_search(category="movies",title="ballballiball")
    #print(_xml_print(response))


# main
#
if __name__ == '__main__':

    from xbmcswift2 import Plugin

    plugin = Plugin()
    plugin.log.setLevel('DEBUG')

    api = AIVApi(plugin)
    _test()

